//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
using System;

using AGG;
using AGG.VertexSource;
using AGG.RasterizerScanline;

namespace AGG.Image
{
    public class ImageBufferFloat : IImageFloat
    {
        public const int OrderB = 0;
        public const int OrderG = 1;
        public const int OrderR = 2;
        public const int OrderA = 3;

        internal class InternalImageGraphics2D : ImageGraphics2D
        {
            ImageBufferFloat m_Owner;

            internal InternalImageGraphics2D(ImageBufferFloat owner)
                : base()
            {
                m_Owner = owner;

                ScanlineRasterizer rasterizer = new ScanlineRasterizer();
                ImageClippingProxyFloat imageClippingProxy = new ImageClippingProxyFloat(owner);

                Initialize(imageClippingProxy, rasterizer);
                ScanlineCache = new ScanlineCachePacked8();
            }
        };

        protected int[] m_yTable;
        protected int[] m_xTable;
        private float[] m_FloatBuffer;
        int m_BufferOffset; // the beggining of the image in this buffer
        int m_BufferFirstPixel; // Pointer to first pixel depending on strideInFloats and image position

        int m_Width;  // Width in pixels
        int m_Height; // Height in pixels
        int m_StrideInFloats; // Number of bytes per row. Can be < 0
        int m_DistanceInFloatsBetweenPixelsInclusive;
        int m_BitDepth;
        Vector2D m_OriginOffset = new Vector2D(0, 0);

        private IBlenderFloat m_Blender;

        public ImageBufferFloat()
        {
        }

        public ImageBufferFloat(IBlenderFloat blender)
        {
            SetBlender(blender);
        }

        public ImageBufferFloat(IImageFloat sourceImage, IBlenderFloat blender)
        {
            SetDimmensionAndFormat(sourceImage.Width, sourceImage.Height, sourceImage.StrideInFloats(), sourceImage.BitDepth, sourceImage.GetDistanceBetweenPixelsInclusive());
            int offset = sourceImage.GetBufferOffsetXY(0, 0);
            float[] buffer = sourceImage.GetBuffer();
            float[] newBuffer = new float[buffer.Length];
            agg_basics.memcpy(newBuffer, offset, buffer, offset, buffer.Length - offset);
            SetBuffer(newBuffer, offset);
            SetBlender(blender);
        }

        public ImageBufferFloat(int width, int height, int bitsPerPixel, IBlenderFloat blender)
        {
            Allocate(width, height, width * (bitsPerPixel / 32), bitsPerPixel);
            SetBlender(blender);
        }

#if false
        public ImageBuffer(IImageFloat image, IBlenderFloat blender, GammaLookUpTable gammaTable)
        {
            unsafe
            {
                AttachBuffer(image.GetBuffer(), image.Width, image.Height, image.StrideInBytes(), image.BitDepth, image.GetDistanceBetweenPixelsInclusive());
            }

            SetBlender(blender);
        }
#endif

        public ImageBufferFloat(IImageFloat sourceImageToCopy, IBlenderFloat blender, int distanceBetweenPixelsInclusive, int bufferOffset, int bitsPerPixel)
        {
            SetDimmensionAndFormat(sourceImageToCopy.Width, sourceImageToCopy.Height, sourceImageToCopy.StrideInFloats(), bitsPerPixel, distanceBetweenPixelsInclusive);
            int offset = sourceImageToCopy.GetBufferOffsetXY(0, 0);
            float[] buffer = sourceImageToCopy.GetBuffer();
            float[] newBuffer = new float[buffer.Length];
            throw new NotImplementedException();
            //agg_basics.memcpy(newBuffer, offset, buffer, offset, buffer.Length - offset);
            SetBuffer(newBuffer, offset + bufferOffset);
            SetBlender(blender);
        }

        public void AttachBuffer(float[] buffer, int bufferOffset, int width, int height, int strideInBytes, int bitDepth, int distanceInBytesBetweenPixelsInclusive)
        {
            m_FloatBuffer = null;
            SetDimmensionAndFormat(width, height, strideInBytes, bitDepth, distanceInBytesBetweenPixelsInclusive);
            SetBuffer(buffer, bufferOffset);
        }

        public void Attach(IImageFloat sourceImage, IBlenderFloat blender, int distanceBetweenPixelsInclusive, int bufferOffset, int bitsPerPixel)
        {
            SetDimmensionAndFormat(sourceImage.Width, sourceImage.Height, sourceImage.StrideInFloats(), bitsPerPixel, distanceBetweenPixelsInclusive);
            int offset = sourceImage.GetBufferOffsetXY(0, 0);
            float[] buffer = sourceImage.GetBuffer();
            SetBuffer(buffer, offset + bufferOffset);
            SetBlender(blender);
        }

        public void Attach(IImageFloat sourceImage, IBlenderFloat blender)
        {
            Attach(sourceImage, blender, sourceImage.GetDistanceBetweenPixelsInclusive(), 0, sourceImage.BitDepth);
        }

        public bool Attach(IImageFloat sourceImage, int x1, int y1, int x2, int y2)
        {
            m_FloatBuffer = null;
            DettachBuffer();

            if (x1 > x2 || y1 > y2)
            {
                throw new Exception("You need to have your x1 and y1 be the lower left corner of your sub image.");
            }
            rect_i boundsRect = new rect_i(x1, y1, x2, y2);
            if (boundsRect.clip(new rect_i(0, 0, (int)sourceImage.Width - 1, (int)sourceImage.Height - 1)))
            {
                SetDimmensionAndFormat(boundsRect.Width, boundsRect.Height, sourceImage.StrideInFloats(), sourceImage.BitDepth, sourceImage.GetDistanceBetweenPixelsInclusive());
                int bufferOffset = sourceImage.GetBufferOffsetXY(boundsRect.x1, boundsRect.y1);
                float[] buffer = sourceImage.GetBuffer();
                SetBuffer(buffer, bufferOffset);
                return true;
            }

            return false;
        }

        public void SetAlpha(byte value)
        {
            if (BitDepth != 32)
            {
                throw new Exception("You don't have alpha channel to set.  Your image has a bit depth of " + BitDepth.ToString() + ".");
            }
            int numPixels = Width * Height;
            int offset;
            float[] buffer = GetBuffer(out offset);
            for (int i = 0; i < numPixels; i++)
            {
                buffer[offset + i * 4 + 3] = value;
            }
        }

        private void Deallocate()
        {
            m_FloatBuffer = null;
            SetDimmensionAndFormat(0, 0, 0, 32, 4);
        }

        public void Allocate(int inWidth, int inHeight, int inScanWidthInFloats, int bitsPerPixel)
        {
            if (bitsPerPixel != 128 && bitsPerPixel != 96 && bitsPerPixel != 32)
            {
                throw new Exception("Unsupported bits per pixel.");
            }
            if (inScanWidthInFloats < inWidth * (bitsPerPixel / 32))
            {
                throw new Exception("Your scan width is not big enough to hold your width and height.");
            }
            SetDimmensionAndFormat(inWidth, inHeight, inScanWidthInFloats, bitsPerPixel, bitsPerPixel / 32);

            m_FloatBuffer = new float[m_StrideInFloats * m_Height];

            SetUpLookupTables();
        }

        public Graphics2D NewGraphics2D()
        {
            InternalImageGraphics2D imageRenderer = new InternalImageGraphics2D(this);

            imageRenderer.Rasterizer.SetVectorClipBox(0, 0, Width, Height);

            return imageRenderer;
        }

        public void CopyFrom(IImageFloat sourceImage)
        {
            CopyFrom(sourceImage, sourceImage.GetBounds(), 0, 0);
        }

        protected void CopyFromNoClipping(IImageFloat sourceImage, rect_i clippedSourceImageRect, int destXOffset, int destYOffset)
        {
            if (GetDistanceBetweenPixelsInclusive() != BitDepth / 32
                || sourceImage.GetDistanceBetweenPixelsInclusive() != sourceImage.BitDepth / 32)
            {
                throw new Exception("WIP we only support packed pixel formats at this time.");
            }

            if (BitDepth == sourceImage.BitDepth)
            {
                int lengthInFloats = clippedSourceImageRect.Width * GetDistanceBetweenPixelsInclusive();

                int sourceOffset = sourceImage.GetBufferOffsetXY(clippedSourceImageRect.x1, clippedSourceImageRect.y1);
                float[] sourceBuffer = sourceImage.GetBuffer();
                int destOffset;
                float[] destBuffer = GetPixelPointerXY(clippedSourceImageRect.x1 + destXOffset, clippedSourceImageRect.y1 + destYOffset, out destOffset);

                for (int i = 0; i<clippedSourceImageRect.Height; i++)
                {
                    agg_basics.memmove(destBuffer, destOffset, sourceBuffer, sourceOffset, lengthInFloats);
                    sourceOffset += sourceImage.StrideInFloats();
                    destOffset += StrideInFloats();
                }
            }
            else
            {
                bool haveConversion = true;
                switch (sourceImage.BitDepth)
                {
                    case 24:
                        switch (BitDepth)
                        {
                            case 32:
                                {
                                    int numPixelsToCopy = clippedSourceImageRect.Width;
                                    for (int i = clippedSourceImageRect.y1; i < clippedSourceImageRect.y2; i++)
                                    {
                                        int sourceOffset = sourceImage.GetBufferOffsetXY(clippedSourceImageRect.x1, clippedSourceImageRect.y1 + i);
                                        float[] sourceBuffer = sourceImage.GetBuffer();
                                        int destOffset;
                                        float[] destBuffer = GetPixelPointerXY(
                                            clippedSourceImageRect.x1 + destXOffset,
                                            clippedSourceImageRect.y1 + i + destYOffset,
                                            out destOffset);
                                        for (int x = 0; x < numPixelsToCopy; x++)
                                        {
                                            destBuffer[destOffset++] = sourceBuffer[sourceOffset++];
                                            destBuffer[destOffset++] = sourceBuffer[sourceOffset++];
                                            destBuffer[destOffset++] = sourceBuffer[sourceOffset++];
                                            destBuffer[destOffset++] = 255;
                                        }
                                    }
                                }
                                break;

                            default:
                                haveConversion = false;
                                break;
                        }
                        break;

                    default:
                        haveConversion = false;
                        break;
                }

                if (!haveConversion)
                {
                    throw new NotImplementedException("You need to write the " + sourceImage.BitDepth.ToString() + " to " + BitDepth.ToString() + " conversion");
                }
            }
        }

        public void CopyFrom(IImageFloat sourceImage, rect_i sourceImageRect, int destXOffset, int destYOffset)
        {
            rect_i sourceImageBounds = sourceImage.GetBounds();
            rect_i clippedSourceImageRect = new rect_i();
            if (clippedSourceImageRect.intersect_rectangles(sourceImageRect, sourceImageBounds))
            {
                rect_i destImageRect = clippedSourceImageRect;
                destImageRect.Offset(destXOffset, destYOffset);
                rect_i destImageBounds = GetBounds();
                rect_i clippedDestImageRect = new rect_i();
                if (clippedDestImageRect.intersect_rectangles(destImageRect, destImageBounds))
                {
                    // we need to make sure the source is also clipped to the dest. So, we'll copy this back to source and offset it.
                    clippedSourceImageRect = clippedDestImageRect;
                    clippedSourceImageRect.Offset(-destXOffset, -destYOffset);
                    CopyFromNoClipping(sourceImage, clippedSourceImageRect, destXOffset, destYOffset);
                }
            }
        }

        public Vector2D OriginOffset
        {
            get { return m_OriginOffset; }
            set { m_OriginOffset = value; }
        }

        public int Width { get { return m_Width; } }
        public int Height { get { return m_Height; } }
        public int StrideInFloats() { return m_StrideInFloats; }
        public int StrideInFloatsAbs() { return System.Math.Abs(m_StrideInFloats); }
        public int GetDistanceBetweenPixelsInclusive() { return m_DistanceInFloatsBetweenPixelsInclusive; }
        public int BitDepth
        {
            get { return m_BitDepth; }
        }

        public virtual rect_i GetBounds()
        {
            return new rect_i(-(int)m_OriginOffset.x, -(int)m_OriginOffset.y, Width - (int)m_OriginOffset.x, Height - (int)m_OriginOffset.y);
        }

        public IBlenderFloat GetBlender()
        {
            return m_Blender;
        }

        public void SetBlender(IBlenderFloat value)
        {
            if (value != null && value.NumPixelBits != BitDepth)
            {
                throw new NotSupportedException("The blender has to support the bit depth of this image.");
            }
            m_Blender = value;
        }

        private void SetUpLookupTables()
        {
            m_yTable = new int[m_Height];
            for (int i = 0; i < m_Height; i++)
            {
                m_yTable[i] = i * m_StrideInFloats;
            }

            m_xTable = new int[m_Width];
            for (int i = 0; i < m_Width; i++)
            {
                m_xTable[i] = i * m_DistanceInFloatsBetweenPixelsInclusive;
            }
        }

        public void FlipY()
        {
            m_StrideInFloats *= -1;
            m_BufferFirstPixel = m_BufferOffset;
            if (m_StrideInFloats < 0)
            {
                int addAmount = -((int)((int)m_Height - 1) * m_StrideInFloats);
                m_BufferFirstPixel = addAmount + m_BufferOffset;
            }

            SetUpLookupTables();
        }

        public void SetBuffer(float[] floatBuffer, int bufferOffset)
        {
            if (floatBuffer.Length < m_Height * m_StrideInFloats)
            {
                throw new Exception("Your buffer does not have enough room it it for your height and strideInBytes.");
            }
            m_FloatBuffer = floatBuffer;
            m_BufferOffset = m_BufferFirstPixel = bufferOffset;
            if (m_StrideInFloats < 0)
            {
                int addAmount = -((int)((int)m_Height - 1) * m_StrideInFloats);
                m_BufferFirstPixel = addAmount + m_BufferOffset;
            }
            SetUpLookupTables();
        }

        private void SetDimmensionAndFormat(int width, int height, int strideInFloats, int bitDepth, int distanceInFloatsBetweenPixelsInclusive)
        {
            if (m_FloatBuffer != null)
            {
                throw new Exception("You already have a buffer set. You need to set dimmensoins before the buffer.  You may need to clear the buffer first.");
            }
            m_Width = width;
            m_Height = height;
            m_StrideInFloats = strideInFloats;
            m_BitDepth = bitDepth;
            if (distanceInFloatsBetweenPixelsInclusive > 4)
            {
                throw new System.Exception("It looks like you are passing bits per pixel rather than distance in Floats.");
            }
            if (distanceInFloatsBetweenPixelsInclusive < (bitDepth / 32))
            {
                throw new Exception("You do not have enough room between pixels to support your bit depth.");
            }
            m_DistanceInFloatsBetweenPixelsInclusive = distanceInFloatsBetweenPixelsInclusive;
            if (strideInFloats < distanceInFloatsBetweenPixelsInclusive * width)
            {
                throw new Exception("You do not have enough strideInFloats to hold the width and pixel distance you have described.");
            }
        }

        public void DettachBuffer()
        {
            m_FloatBuffer = null;
            m_Width = m_Height = m_StrideInFloats = m_DistanceInFloatsBetweenPixelsInclusive = 0;
        }

        public float[] GetBuffer()
        {
            return m_FloatBuffer;
        }

        public float[] GetBuffer(out int bufferOffset)
        {
            bufferOffset = m_BufferOffset;
            return m_FloatBuffer;
        }

        public float[] GetPixelPointerY(int y, out int bufferOffset)
        {
            bufferOffset = m_BufferFirstPixel + m_yTable[y];
            //bufferOffset = GetBufferOffsetXY(0, y);
            return m_FloatBuffer;
        }

        public float[] GetPixelPointerXY(int x, int y, out int bufferOffset)
        {
            bufferOffset = GetBufferOffsetXY(x, y);
            return m_FloatBuffer;
        }

        public RGBA_Floats pixel(int x, int y)
        {
            return m_Blender.PixelToColorRGBA_Floats(m_FloatBuffer, GetBufferOffsetXY(x, y));
        }

        public int GetBufferOffsetY(int y)
        {
            return m_BufferFirstPixel + m_yTable[y];
        }

        public int GetBufferOffsetXY(int x, int y)
        {
            return m_BufferFirstPixel + m_yTable[y] + m_xTable[x];
        }

        public void copy_pixel(int x, int y, float[] c, int ByteOffset)
        {
            throw new System.NotImplementedException();
            //byte* p = GetPixelPointerXY(x, y);
            //((int*)p)[0] = ((int*)c)[0];
            //p[OrderR] = c.r;
            //p[OrderG] = c.g;
            //p[OrderB] = c.b;
            //p[OrderA] = c.a;
        }

        public void BlendPixel(int x, int y, RGBA_Floats c, byte cover)
        {
            throw new System.NotImplementedException();
            /*
            cob_type::copy_or_blend_pix(
                (value_type*)m_rbuf->row_ptr(x, y, 1)  + x + x + x, 
                c.r, c.g, c.b, c.a, 
                cover);*/
        }

        public void SetPixelFromColor(float[] destPixel, IColorType c)
        {
            throw new System.NotImplementedException();
            //pDestPixel[OrderR] = (byte)c.R_Byte;
            //pDestPixel[OrderG] = (byte)c.G_Byte;
            //pDestPixel[OrderB] = (byte)c.B_Byte;
        }

        public void copy_hline(int x, int y, int len, RGBA_Floats sourceColor)
        {
            int bufferOffset;
            float[] buffer = GetPixelPointerXY(x, y, out bufferOffset);

            m_Blender.CopyPixels(buffer, bufferOffset, sourceColor, len);
        }

        public void copy_vline(int x, int y, int len, RGBA_Floats sourceColor)
        {
            throw new NotImplementedException();
#if false
            int scanWidth = StrideInBytes();
            byte* pDestBuffer = GetPixelPointerXY(x, y);
            do
            {
                m_Blender.CopyPixel(pDestBuffer, sourceColor);
                pDestBuffer = &pDestBuffer[scanWidth];
            }
            while (--len != 0);
#endif
        }


        public void blend_hline(int x1, int y, int x2, RGBA_Floats sourceColor, byte cover)
        {
            if (sourceColor.m_A != 0)
            {
                int len = x2 - x1 + 1;

                int bufferOffset;
                float[] buffer = GetPixelPointerXY(x1, y, out bufferOffset);

                float alpha = sourceColor.m_A * (cover * (1.0f / 255.0f));
                if (alpha == 1)
                {
                    m_Blender.CopyPixels(buffer, bufferOffset, sourceColor, len);
                }
                else
                {
                    do
                    {
                        m_Blender.BlendPixel(buffer, bufferOffset, new RGBA_Floats(sourceColor.m_R, sourceColor.m_G, sourceColor.m_B, alpha));
                        bufferOffset += m_DistanceInFloatsBetweenPixelsInclusive;
                    }
                    while (--len != 0);
                }
            }
        }

        public void blend_vline(int x, int y1, int y2, RGBA_Floats sourceColor, byte cover)
        {
            throw new NotImplementedException();
#if false
            int ScanWidth = StrideInBytes();
            if (sourceColor.m_A != 0)
            {
                unsafe
                {
                    int len = y2 - y1 + 1;
                    byte* p = GetPixelPointerXY(x, y1);
                    sourceColor.m_A = (byte)(((int)(sourceColor.m_A) * (cover + 1)) >> 8);
                    if (sourceColor.m_A == base_mask)
                    {
                        byte cr = sourceColor.m_R;
                        byte cg = sourceColor.m_G;
                        byte cb = sourceColor.m_B;
                        do
                        {
                            m_Blender.CopyPixel(p, sourceColor);
                            p = &p[ScanWidth];
                        }
                        while (--len != 0);
                    }
                    else
                    {
                        if (cover == 255)
                        {
                            do
                            {
                                m_Blender.BlendPixel(p, sourceColor);
                                p = &p[ScanWidth];
                            }
                            while (--len != 0);
                        }
                        else
                        {
                            do
                            {
                                m_Blender.BlendPixel(p, sourceColor);
                                p = &p[ScanWidth];
                            }
                            while (--len != 0);
                        }
                    }
                }
            }
#endif
        }

        public void blend_solid_hspan(int x, int y, int len, RGBA_Floats sourceColor, byte[] covers, int coversIndex)
        {
            float colorAlpha = sourceColor.m_A;
            if (colorAlpha != 0)
            {
                unchecked
                {
                    int bufferOffset;
                    float[] buffer = GetPixelPointerXY(x, y, out bufferOffset);

                    do
                    {
                        float alpha = colorAlpha * (covers[coversIndex] * (1.0f/255.0f));
                        if (alpha == 1)
                        {
                            m_Blender.CopyPixels(buffer, bufferOffset, sourceColor, 1);
                        }
                        else
                        {
                            m_Blender.BlendPixel(buffer, bufferOffset, new RGBA_Floats(sourceColor.m_R, sourceColor.m_G, sourceColor.m_B, alpha));
                        }
                        bufferOffset += m_DistanceInFloatsBetweenPixelsInclusive;
                        coversIndex++;
                    }
                    while (--len != 0);
                }
            }
        }

        public void blend_solid_vspan(int x, int y, int len, RGBA_Floats c, byte[] covers, int coversIndex)
        {
            throw new NotImplementedException();
#if false
            if (sourceColor.m_A != 0)
            {
                int ScanWidth = StrideInBytes();
                unchecked
                {
                    byte* p = GetPixelPointerXY(x, y);
                    do
                    {
                        byte oldAlpha = sourceColor.m_A;
                        sourceColor.m_A = (byte)(((int)(sourceColor.m_A) * ((int)(*covers++) + 1)) >> 8);
                        if (sourceColor.m_A == base_mask)
                        {
                            m_Blender.CopyPixel(p, sourceColor);
                        }
                        else
                        {
                            m_Blender.BlendPixel(p, sourceColor);
                        }
                        p = &p[ScanWidth];
                        sourceColor.m_A = oldAlpha;
                    }
                    while (--len != 0);
                }
            }
#endif
        }

        public void copy_color_hspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex)
        {
            int bufferOffset = GetBufferOffsetXY(x, y);

            do
            {
                m_Blender.CopyPixels(m_FloatBuffer, bufferOffset, colors[colorsIndex], 1);

                ++colorsIndex;
                bufferOffset += m_DistanceInFloatsBetweenPixelsInclusive;
            }
            while (--len != 0);
        }

        public void copy_color_vspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex)
        {
            int bufferOffset = GetBufferOffsetXY(x, y);

            do
            {
                m_Blender.CopyPixels(m_FloatBuffer, bufferOffset, colors[colorsIndex], 1);

                ++colorsIndex;
                bufferOffset += m_StrideInFloats;
            }
            while (--len != 0);
        }

        public void blend_color_hspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            int bufferOffset = GetBufferOffsetXY(x, y);
            m_Blender.BlendPixels(m_FloatBuffer, bufferOffset, colors, colorsIndex, covers, coversIndex, firstCoverForAll, len);
        }

        public void blend_color_vspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            int bufferOffset = GetBufferOffsetXY(x, y);

            int ScanWidth = StrideInFloatsAbs();
            if (!firstCoverForAll)
            {
                do
                {
                    DoCopyOrBlendFloat.BasedOnAlphaAndCover(m_Blender, m_FloatBuffer, bufferOffset, colors[colorsIndex], covers[coversIndex++]);
                    bufferOffset += ScanWidth;
                    ++colorsIndex;
                }
                while (--len != 0);
            }
            else
            {
                if (covers[coversIndex] == 1)
                {
                    do
                    {
                        DoCopyOrBlendFloat.BasedOnAlpha(m_Blender, m_FloatBuffer, bufferOffset, colors[colorsIndex]);
                        bufferOffset += ScanWidth;
                        ++colorsIndex;
                    }
                    while (--len != 0);
                }
                else
                {
                    do
                    {

                        DoCopyOrBlendFloat.BasedOnAlphaAndCover(m_Blender, m_FloatBuffer, bufferOffset, colors[colorsIndex], covers[coversIndex]);
                        bufferOffset += ScanWidth;
                        ++colorsIndex;
                    }
                    while (--len != 0);
                }
            }
        }

        public void apply_gamma_inv(GammaLookUpTable g)
        {
            throw new System.NotImplementedException();
            //for_each_pixel(apply_gamma_inv_rgba<color_type, order_type, GammaLut>(g));
        }

        private bool IsPixelVisible(int x, int y)
        {
            RGBA_Floats pixelValue = GetBlender().PixelToColorRGBA_Floats(m_FloatBuffer, GetBufferOffsetXY(x, y));
            return (pixelValue.A_Byte != 0 || pixelValue.R_Byte != 0 || pixelValue.G_Byte != 0 || pixelValue.B_Byte != 0);
        }

        public void GetVisibleBounds(out rect_i visibleBounds)
        {
            visibleBounds = new rect_i(0, 0, Width, Height);

            // trim the bottom
            bool aPixelsIsVisible = false;
            for (int y = 0; y < m_Height; y++)
            {
                for (int x = 0; x < m_Width; x++)
                {
                    if (IsPixelVisible(x, y))
                    {
                        visibleBounds.Bottom = y;
                        y = m_Height;
                        x = m_Width;
                        aPixelsIsVisible = true;
                    }
                }
            }

            // if we don't run into any pixels set for the top trim than there are no pixels set at all
            if (!aPixelsIsVisible)
            {
                visibleBounds.SetRect(0, 0, 0, 0);
                return;
            }

            // trim the bottom
            for (int y = m_Height - 1; y >= 0; y--)
            {
                for (int x = 0; x < m_Width; x++)
                {
                    if (IsPixelVisible(x, y))
                    {
                        visibleBounds.Top = y + 1;
                        y = -1;
                        x = m_Width;
                    }
                }
            }

            // trim the left
            for (int x = 0; x < m_Width; x++)
            {
                for (int y = 0; y < m_Height; y++)
                {
                    if (IsPixelVisible(x, y))
                    {
                        visibleBounds.Left = x;
                        y = m_Height;
                        x = m_Width;
                    }
                }
            }

            // trim the right
            for (int x = m_Width - 1; x >= 0; x--)
            {
                for (int y = 0; y < m_Height; y++)
                {
                    if (IsPixelVisible(x, y))
                    {
                        visibleBounds.Right = x + 1;
                        y = m_Height;
                        x = -1;
                    }
                }
            }
        }

        public void CropToVisible()
        {
            Vector2D OldOriginOffset = OriginOffset;

            //Move the HotSpot to 0, 0 so PPoint will work the way we want
            OriginOffset = new Vector2D(0, 0);

            rect_i visibleBounds;
            GetVisibleBounds(out visibleBounds);

            if (visibleBounds.Width == Width
                && visibleBounds.Height == Height)
            {
                OriginOffset = OldOriginOffset;
                return;
            }

            // check if the Not0Rect has any size
            if (visibleBounds.Width > 0)
            {
                ImageBufferFloat TempImage = new ImageBufferFloat();

                // set TempImage equal to the Not0Rect
                TempImage.Initialize(this, visibleBounds);

                // set the frame equal to the TempImage
                Initialize(TempImage);

                OriginOffset = new Vector2D(-visibleBounds.Left + OldOriginOffset.x, -visibleBounds.Bottom + OldOriginOffset.y);
            }
            else
            {
                Deallocate();
            }
        }

        public rect_i GetBoundingRect()
        {
            rect_i boundingRect = new rect_i(0, 0, Width, Height);
            boundingRect.Offset((int)OriginOffset.x, (int)OriginOffset.y);
            return boundingRect;
        }

        private void Initialize(ImageBufferFloat sourceImage)
        {
            rect_i sourceBoundingRect = sourceImage.GetBoundingRect();

            Initialize(sourceImage, sourceBoundingRect);
            OriginOffset = sourceImage.OriginOffset;
        }

        private void Initialize(ImageBufferFloat sourceImage, rect_i boundsToCopyFrom)
        {
            if (sourceImage == this)
            {
                throw new Exception("We do not create a temp buffer for this to work.  You must have a source distinct from the dest.");
            }
            Deallocate();
            Allocate(boundsToCopyFrom.Width, boundsToCopyFrom.Height, boundsToCopyFrom.Width * sourceImage.BitDepth / 8, sourceImage.BitDepth);
            SetBlender(sourceImage.GetBlender());

            if (m_Width != 0 && m_Height != 0)
            {
                rect_i DestRect = new rect_i(0, 0, boundsToCopyFrom.Width, boundsToCopyFrom.Height);
                rect_i AbsoluteSourceRect = boundsToCopyFrom;
                // The first thing we need to do is make sure the frame is cleared. LBB [3/15/2004]
                Graphics2D graphics2D = NewGraphics2D();
                graphics2D.Clear(new RGBA_Floats(0, 0, 0, 0));

                int x = -boundsToCopyFrom.Left - (int)sourceImage.OriginOffset.x;
                int y = -boundsToCopyFrom.Bottom - (int)sourceImage.OriginOffset.y;

                graphics2D.Render(sourceImage, x, y, 0, 1, 1, new RGBA_Floats(1, 1, 1, 1), ImageGraphics2D.BlendMode.USE_IMAGE_BLENDER);
            }
        }
    }

    public static class DoCopyOrBlendFloat
    {
        const byte base_mask = 255;

        public static void BasedOnAlpha(IBlenderFloat Blender, float[] destBuffer, int bufferOffset, RGBA_Floats sourceColor)
        {
            //if (sourceColor.m_A != 0)
            {
#if false // we blend regardless of the alpha so that we can get Light Opacity working (used this way we have addative and faster blending in one blender) LBB
                if (sourceColor.m_A == base_mask)
                {
                    Blender.CopyPixel(pDestBuffer, sourceColor);
                }
                else
#endif
                {
                    Blender.BlendPixel(destBuffer, bufferOffset, sourceColor);
                }
            }
        }

        public static void BasedOnAlphaAndCover(IBlenderFloat Blender, float[] destBuffer, int bufferOffset, RGBA_Floats sourceColor, int cover)
        {
            if (cover == 255)
            {
                BasedOnAlpha(Blender, destBuffer, bufferOffset, sourceColor);
            }
            else
            {
                //if (sourceColor.m_A != 0)
                {
                    sourceColor.m_A = sourceColor.m_A * ((float)cover * (1/255));
#if false // we blend regardless of the alpha so that we can get Light Opacity working (used this way we have addative and faster blending in one blender) LBB
                    if (sourceColor.m_A == base_mask)
                    {
                        Blender.CopyPixel(pDestBuffer, sourceColor);
                    }
                    else
#endif
                    {
                        Blender.BlendPixel(destBuffer, bufferOffset, sourceColor);
                    }
                }
            }
        }
    };
}